package com.pay.interceptor;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.util.WebUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.pay.common.util.PropertyUtil;
import com.pay.constants.RedisKeyConstants;
import com.pay.constants.WeixinConstants;
import com.pay.constants.MposWxFrontConstans;
import com.pay.dubbo.wx.WxServiceFacade;
import com.pay.dubbo.wx.WxUserInfoServiceFacade;
import com.pay.entity.wx.WxUserInfo;
import com.pay.util.EmojiFilter;
import com.pay.util.NeedOpenId;
import com.pay.util.RedisUtil;
import com.pay.util.RequestUtil;

/**
 * @Description: 微信拦截器获取openId
 * @see: Oauth2AccessTokenInterceptor 此处填写需要参考的类
 * @version 2016年9月19日 上午10:47:20
 * @author shulin.feng
 */
@Repository
public class Oauth2AccessTokenInterceptor extends HandlerInterceptorAdapter {

	private static Logger logger = LoggerFactory.getLogger(Oauth2AccessTokenInterceptor.class);

	@Resource
	private WxServiceFacade wxServiceFacade;
	@Resource
	private WxUserInfoServiceFacade wxUserInfoServiceFacade;
	private static final String LOGIN_URL = "/loginPage/loginPage.html";

	private static String ysappid;
	private static String ysAppsecret;
	private static String staticResHomeUrl;
	static {
		PropertyUtil propertyUtil = PropertyUtil.getInstance("wxweb");
		ysappid = propertyUtil.getProperty("ysAppid");
		ysAppsecret = propertyUtil.getProperty("ysAppsecret");
		staticResHomeUrl = propertyUtil.getProperty("com.pay.wx.staticResHomeUrl");
	}

	/**
	 * 在请求处理之前获取openId
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		HandlerMethod handlerMethod = null;
		try {
			handlerMethod = (HandlerMethod) handler;
		} catch (Exception e) {
			logger.error("Oauth2AccessTokenAnnotationInterceptor 异常信息:{}", e);
		}

		if (handlerMethod == null) { // 如果请求地址错误,则将该错误抛入springmvc处理，返回404的地址错误提示
			return super.preHandle(request, response, handler);
		}

		Method method = handlerMethod.getMethod();
		NeedOpenId annotation = method.getAnnotation(NeedOpenId.class);
		if (annotation == null) {
			return super.preHandle(request, response, handler);
		}
		boolean oauth2Flag = annotation.oauth2Flag();

		if (annotation.needOpenId()) {
			boolean flag = processAuth2OpenId(request, response, oauth2Flag, annotation);
			if (!flag) {
				return false;
			}
		}
		return super.preHandle(request, response, handler);
	}

	/**
	 * @Description 授权获取openId
	 * @param request
	 * @param response
	 * @param oauth2Flag
	 * @param annotation
	 * @return
	 * @throws IOException
	 * @see 需要参考的类或方法
	 */
	private boolean processAuth2OpenId(HttpServletRequest request, HttpServletResponse response, boolean oauth2Flag,
			NeedOpenId annotation) throws IOException {
		// 取session中是否存在openid
		String openId = request.getParameter("openId");
		logger.info("缓存中微信openId:{}", openId);
		String requestURL = request.getRequestURL().toString();
		
		requestURL = requestURL.replace(requestURL.substring(0, requestURL.indexOf(":")), "https");

		// 首先验证缓存中是否存在openid，如果为空进行授权，反则直接跳过
		if (StringUtils.isBlank(openId)) {
			String code = request.getParameter("code");

			// 获取请求参数，移除code和state
			String requestParammStr = RequestUtil.getRequestOauthParammStr(request);
			String redirectUrl = requestURL.replace(";", ",");
			if (!StringUtils.isEmpty(requestParammStr)) {
				redirectUrl += "?" + requestParammStr;
			}

			// 验证code是否为空（第一步：用户同意授权，获取code）
			if (StringUtils.isNotBlank(code)) {
				long tokenTime = System.currentTimeMillis();
				// 第二步：通过code换取网页授权access_token
				String accessTokenJson = getOauth2AccessToken(code);
				logger.info("获取微信accessToken  openId:{},accessToken：{}", openId,accessTokenJson);
				if (accessTokenJson != null) {
					JSONObject obj = JSON.parseObject(accessTokenJson);

					// 获取失败，重新跳转到授权页面
					if (obj.containsKey("errcode")) {
						String errcode = obj.getString("errcode");
						if (errcode.equals("40029")) {
							String oauthUrl = oauthUrl(redirectUrl, oauth2Flag);
							response.sendRedirect(response.encodeRedirectURL(oauthUrl));
							return false;
						}
					}
					openId = obj.getString("openid");
					WebUtils.setSessionAttribute(request, WeixinConstants.sessionOpenIdKey, openId);
					String accessToken = obj.getString("access_token");

					// 第三步：拉取用户信息(需scope为 snsapi_userinfo)
					if (oauth2Flag) {
						getWeixinInfo(accessToken, openId, request);
					}
				} else {
					logger.info("获取accessToken返回null，服务异常，重新进入授权页面");
					String oauthUrl = oauthUrl(redirectUrl, oauth2Flag);
					response.sendRedirect(response.encodeRedirectURL(oauthUrl));
					return false;
				}

				logger.info("拦截器调用微信授权耗时:{}ms", System.currentTimeMillis() - tokenTime);
			} else {
				// 跳转到授权页面，获取code
				String oauthUrl = oauthUrl(redirectUrl, oauth2Flag);
				response.sendRedirect(response.encodeRedirectURL(oauthUrl));
				return false;
			}
		}
		logger.info("拦截器获取openId:{},设备类型：{},ip:{}", openId, request.getHeader("user-agent"),
				RequestUtil.getIpAddr(request));
		// 判定是否需要登录验证
		if (annotation.isLoginFlag()) {
			String customerNo = RedisUtil.getValue(RedisKeyConstants.MULT_ACCOUNT_DEFAULT_OPENID_USERNO + openId);
			logger.info("查询到的customerNo：" + customerNo);
			if (StringUtils.isBlank(customerNo)) {
				logger.info("多账户中未查到登录帐户");
				logger.info("openId:{}尚未进行过绑定，请重新登录", openId);
				sendRedirectLogin(request, response, requestURL,openId);
				return false;
			}
			request.setAttribute("customerNo", customerNo);
		}
		return true;
	}

	/**
	 * 用户同意授权，获取code
	 *
	 * @param url
	 * @param appId
	 * @param oauth2Flag
	 * @return
	 */
	private String oauthUrl(String url, boolean oauth2Flag) {
		StringBuffer oathUrl = new StringBuffer();
		String encodeUrl = "";
		if (url != null) {
			try {
				encodeUrl = URLEncoder.encode(url, "utf-8");
			} catch (UnsupportedEncodingException e) {
				logger.error("urlEncode异常!.oauthUrl:{}", e);
			}
		}
		Random random = new Random();
		int nextInt = random.nextInt(10000);
		Calendar cld = Calendar.getInstance();
		logger.info("应用授权作用域：{}", oauth2Flag ? "snsapi_userinfo" : "snsapi_base");
		oathUrl.append(WeixinConstants.oauth2Url).append("?").append("appid=").append(ysappid).append("&redirect_uri=")
				.append(encodeUrl).append("&response_type=").append("code").append("&scope=")
				.append(oauth2Flag ? "snsapi_userinfo" : "snsapi_base").append("&state=")
				.append(cld.getTimeInMillis() + "" + nextInt).append("#wechat_redirect");

		logger.info("用户授权地址:{}", oathUrl.toString());
		return oathUrl.toString();
	}

	// 转发到登录页面
	private void sendRedirectLogin(HttpServletRequest request, HttpServletResponse response, String requestURL,String openId)
			throws IOException {
		String contextPath = request.getContextPath();
//		String homeUrl = requestURL.split(contextPath)[0];
		String servletPath = request.getServletPath();
		String params = RequestUtil.getRequestParammStr(request);

		// 将servletPath放入session中，方便在处理完忘记密码或忘记用户名等业务后跳转到最开始发起的请求页面
		WebUtils.setSessionAttribute(request, MposWxFrontConstans.FIRST_SERVLET_PATH, servletPath + "?" + params);
		logger.info("servletPath=" + servletPath + "?" + params);
		response.sendRedirect(staticResHomeUrl + LOGIN_URL+"?openId="+openId);
	}

	/**
	 * 通过code换取网页授权access_token
	 *
	 * @param code
	 * @param appId
	 * @param appSecret
	 * @param oauth
	 * @return
	 */
	private String getOauth2AccessToken(String code) {
		String content = wxServiceFacade.getOauth2AccessToken(ysappid, ysAppsecret, code);
		return content == null ? null : content;
	}

	/**
	 * 拉取用户信息(需scope为 snsapi_userinfo)
	 *
	 * @param accessToken
	 * @param openId
	 * @param request
	 */
	private void getWeixinInfo(String accessToken, String openId, HttpServletRequest request) {
		String result = wxServiceFacade.getOauth2UserInfo(accessToken, openId);
		logger.info("请求授权获取微信用户信息返回结果：{}", result);
		if (!StringUtils.isEmpty(result)) {
			JSONObject object = JSON.parseObject(result);
			boolean containsKey = object.containsKey("errcode");
			if (!containsKey) {
				WxUserInfo wxUserInfo = wxUserInfoServiceFacade.findWxUserInfoByOpenId(openId);// 判断当前的用户openid是否已经关注.
				if (wxUserInfo != null) {// 已经关注,如果是取消关注的操作,更改关注状态
					wxUserInfo.setCity(EmojiFilter.filterEmoji(object.getString("city")));
					wxUserInfo.setCountry(EmojiFilter.filterEmoji(object.getString("country")));
					wxUserInfo.setHeadimgurl(object.getString("headimgurl"));
					wxUserInfo.setLanguage(object.getString("language"));
					wxUserInfo.setNickname(EmojiFilter.filterEmoji(object.getString("nickname")));
					wxUserInfo.setOpenid(openId);
					wxUserInfo.setProvince(EmojiFilter.filterEmoji(object.getString("province")));
					wxUserInfo.setSex(object.getString("sex"));
					wxUserInfoServiceFacade.updateWxUserInfo(wxUserInfo);
				} else {// 刚进来关注的用户新添加一条数据
					wxUserInfo = new WxUserInfo();
					wxUserInfo.setSubscribe("0");
					wxUserInfo.setCity(EmojiFilter.filterEmoji(object.getString("city")));
					wxUserInfo.setCountry(EmojiFilter.filterEmoji(object.getString("country")));
					wxUserInfo.setCreatetime(new Date());
					wxUserInfo.setHeadimgurl(object.getString("headimgurl"));
					wxUserInfo.setLanguage(object.getString("language"));
					wxUserInfo.setNickname(EmojiFilter.filterEmoji(object.getString("nickname")));
					wxUserInfo.setOpenid(openId);
					wxUserInfo.setProvince(EmojiFilter.filterEmoji(object.getString("province")));
					wxUserInfo.setSex(object.getString("sex"));
					wxUserInfoServiceFacade.insertWxUserInfo(wxUserInfo);
				}
			}
		}
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		super.afterCompletion(request, response, handler, ex);
	}

	@Override
	public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		super.afterConcurrentHandlingStarted(request, response, handler);
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		super.postHandle(request, response, handler, modelAndView);
	}
}
